package prometheuscli

import (
	"context"
	"errors"
	"fmt"
	"log"
	"math"
	"net/http"
	"strings"
	"time"

	"github.com/prometheus/client_golang/api"
	v1 "github.com/prometheus/client_golang/api/prometheus/v1"
	prometheus_model "github.com/prometheus/common/model"
)

var mAPI MetricsAPI

func SetupMetricAPI(address string) (err error) {
	mAPI, err = NewMetricAPI(address)
	if mAPI == nil {
		mAPI = &EmptyMetricsAPI{}
	}
	return err
}

func SetMetricAPI(api MetricsAPI) { //启动默认的prometheus的客户端
	mAPI = api
}

type MetricsAPI interface {
	pqlRangeMatrix(query string, start string, end string, step string) (map[int64]any, error)
	pqlVector(query string, timestamp int64) (float64, error)

	HistogramQuantileLabel() string
}

type PrometheusAPI struct {
	prometheusAddr string
	v1.API
}

func (p *PrometheusAPI) HistogramQuantileLabel() string {
	return "le"
}

type VictoriaMetricsAPI struct {
	*PrometheusAPI
}

func (v *VictoriaMetricsAPI) HistogramQuantileLabel() string {
	return "vmrange"
}

type EmptyMetricsAPI struct {
}

func (e *EmptyMetricsAPI) HistogramQuantileLabel() string {
	return ""
}

func (e *EmptyMetricsAPI) pqlRangeMatrix(query string, start string, end string, step string) (map[int64]any, error) {
	return nil, errors.New("metricsAPI (Prometheus) is not configured yet or init failed")
}

func (e *EmptyMetricsAPI) pqlVector(query string, timestamp int64) (float64, error) {
	return 0, errors.New("metricsAPI (Prometheus) is not configured yet or init failed")
}

func NewMetricAPI(address string) (MetricsAPI, error) {
	if len(address) == 0 {
		return nil, errors.New("prometheus address is unset")
	}

	// SetupGlobalConfig
	client, err := api.NewClient(api.Config{
		Address: address,
	})

	if err != nil {
		return nil, err
	}

	v1Api := v1.NewAPI(client)
	ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
	defer cancel()
	buildInfo, err := v1Api.Buildinfo(ctx)
	if err != nil {
		return nil, fmt.Errorf("prometheus connect failed! ADDR: %s, Err: %s", address, err)
	}

	api := &PrometheusAPI{
		API:            v1Api,
		prometheusAddr: address,
	}

	// 通过访问目标的/vmui/?路径，检查是否是victoria metrics
	resp, err := http.Get(fmt.Sprintf("%s/vmui/?", address))
	if err != nil || resp.StatusCode != 200 {
		// Can not check whether is victoria metrics, use prometheus as default
		log.Printf("Prometheus Version: %s", buildInfo.Version)
		return api, nil
	}

	// TODO How to get version of victoria metrics?
	log.Printf("Using Victoria Metrics, work as Prometheus Version: %s", buildInfo.Version)
	return &VictoriaMetricsAPI{
		PrometheusAPI: api,
	}, nil
}

var timeLayout = "2006-01-02 150405"

// start  2023-09-28 080501
// end 2023-09-28 080501
// step MINUTE
func (api *PrometheusAPI) pqlRangeMatrix(query string, start string, end string, step string) (map[int64]any, error) {
	startTime, err := time.Parse(timeLayout, start)
	if err != nil {
		log.Printf("Failed to parse time[%s], expected Layout[%s],err: %v", start, timeLayout, err)
	}
	endTime, err := time.Parse(timeLayout, end)
	if err != nil {
		log.Printf("Failed to parse time[%s], expected Layout[%s],err: %v", end, timeLayout, err)
	}
	var stepDuration time.Duration = time.Minute
	switch strings.ToUpper(step) {
	case "MINUTE":
		stepDuration = time.Minute
	case "SECOND":
		stepDuration = time.Second
	case "HOUR":
		stepDuration = time.Hour
	}

	timeRange := v1.Range{
		Start: startTime,
		End:   endTime,
		Step:  stepDuration,
	}

	result, warns, err := api.QueryRange(context.Background(), query, timeRange)

	if err != nil {
		log.Printf("failed to query pql, query [%s], range[%s %s %s]", query, start, end, step)
	}

	if len(warns) > 0 {
		log.Printf("Request Prometheus Warning: %s", warns)
	}

	metrix := result.(prometheus_model.Matrix)

	//将开始时间转化为int64类型的时间戳格式
	startUnix_s := startTime.Unix() //开始时间，s级别
	endUnix_s := endTime.Unix()     //结束时间，s级别
	startUnix := startUnix_s * 1000 //时间戳扩大为和v.Timestamp同等的比例。ms级别

	cap := (endUnix_s - startUnix_s) / 60
	var throughputs map[int64]any = make(map[int64]any)
	for i := 0; i <= int(cap); i++ {
		throughputs[startUnix] = 0
		startUnix += 60000
	}

	// TODO
	for _, timeSeries := range metrix {
		// fmt.Print(len(timeSeries.Values))
		for _, v := range timeSeries.Values { //timeSeries.Values是一个[]SamplePair数组，SamplePair中有Timestamp和Value
			//throughput中的数据可能为NaN，所以要进行类型判断
			if math.IsNaN(float64(v.Value)) { //如果为NaN，就将其赋值为0
				throughputs[int64(v.Timestamp)] = 0
			} else {
				throughputs[int64(v.Timestamp)] = float64(v.Value)
			}
		}
	}
	return throughputs, err
}

func (api *PrometheusAPI) pqlVector(query string, timestamp int64) (float64, error) {
	if query == "" {
		return 0, nil
	}
	result, warnings, err := api.Query(context.Background(), query, time.UnixMilli(timestamp/1e6))
	if err != nil {
		return 0, err
	}
	if len(warnings) > 0 {
		log.Printf("Request Prometheus Warning: %s", warnings)
	}
	if vector, ok := result.(prometheus_model.Vector); ok {
		if len(vector) != 1 {
			log.Printf("[x Query Metrics] %s , Size: %d", query, len(vector))
		} else {
			log.Printf("[Query Metric] %s, Value: %f", query, vector[0].Value)
			if math.IsNaN(float64(vector[0].Value)) {
				return 0, nil
			}
			return float64(vector[0].Value), nil
		}
	}
	return 0, nil
}
